<template>
  <div :style="isMobile? '':{width:colWidth*columnCount+'px',left:'50%',marginLeft: -1*colWidth*columnCount/2 +'px'}" class="vue-waterfall-easy clearfix">
    <a v-for="(v,i) in imgsArrC" :href="v.link" :style="{padding:gap/2+'px',width: isMobile ? '' : colWidth+'px'}" class="img-box">
      <div class="img-inner-box">
        <div :style="{width:imgWidthC+'px',height:v.height?v.height+'px':''}" class="img-wraper">
          <img :src="v.src" alt="">
        </div>
        <div class="img-info">
          <slot :index="i" :value="v"></slot>
        </div>
      </div>
    </a>
    <!--<div style="clear: both;"></div>-->
    <div v-if="isPreloadingC" :class="{'first-loading':isFirstTIme}" class="loading">
      <div class="double-bounce1"></div>
      <div class="double-bounce2"></div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'vue-waterfall-easy',
    props: {
      gap: {
        type: Number,
        default: 20
      },
      maxCols: {
        type: Number,
        default: 5
      },
      imgsArr: {
        type: Array,
        required: true
      },
      imgWidth: {
        type: Number,
        default: 240
      },
      timeOut: { // 预加载事件小于500毫秒就不显示加载动画，增加用户体验
        type: Number,
        default: 500
      }
    },
    data () {
      return {
        msg: 'this is from vue-waterfall-easy.vue',
        columnCount: NaN, // 列数，根据窗口大小初始化
        isMobile: navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i), // 初始化移动端
        beginIndex: NaN, // 第二列首张图片的index，从这一张开始重新计算图片位置

        colsHeightArr: [], // 每一列的图片总和高度为元素组成的数组
        imgBoxEls: null, // 所有的.img-box元素
        isPreloading: true, // 预加载状态中（1.以等待图片替换 2.图片全部预加载完显示）
        isPreloadingC: true,
        imgsArrC: [], // 预加载完之后再才开始
        loadedCount: 0, // 已经加载图片数量
        isFirstTIme: true // 首次加载
      }
    },
    computed: {
      colWidth () { // 每一列的宽度
        return this.imgWidth + this.gap
      },
      imgWidthC () { // 对于移动端重新计算图片宽度
        return this.isMobile ? window.innerWidth / 2 - this.gap : this.imgWidth
      }
    },
    methods: {
      waterfall () { // 执行瀑布布局
        for (var i = this.beginIndex; i < this.imgsArr.length; i++) {
          var minHeight = Math.min.apply(null, this.colsHeightArr) // 最低高低
          var minIndex = this.colsHeightArr.indexOf(minHeight) // 最低高度的索引
          var width = this.imgBoxEls[0].offsetWidth // 图片的宽度获取
          // 设置元素定位的位置
          this.imgBoxEls[i].style.position = 'absolute'
          this.imgBoxEls[i].style.left = minIndex * width + 'px'
          this.imgBoxEls[i].style.top = minHeight + 'px'

          // 更新colsHeightArr
          this.$set(this.colsHeightArr, minIndex, minHeight + this.imgBoxEls[i].offsetHeight)
        }
        this.beginIndex = this.imgsArr.length
      },

      loadFn (e, oImg, i) { // 每张图片预加载完成执行函数
        this.loadedCount++
        if (e.type === 'load') { // 使用图片原始宽度计算图片的高度
          this.$set(this.imgsArr[i], 'height', Math.round(this.imgWidthC / (oImg.width / oImg.height)))
        }
        if (this.loadedCount === this.imgsArr.length) {
          this.imgsArrC = this.imgsArr.concat([])

          this.isPreloading = false
          this.isFirstTIme = false

          // 预加载完毕
          this.$nextTick(() => {
            this.initImgBoxEls()
            this.$emit('preloaded')
          })
        }
      },
      preload () {
        this.imgsArr.forEach((v, i) => {
          if (i < this.loadedCount) return

          var oImg = new Image()
          oImg.addEventListener('load', (e) => {
            this.loadFn(e, oImg, i)
          })
          oImg.src = v.src
        })
      },
      // -----------------初始化化------------------------
      initColsHeightArr () { // 第一行元素的高度组成的数组-初始化
        this.colsHeightArr = [] // 列数发生变化重新初始化
        for (var i = 0; i < this.columnCount; i++) {
          this.imgBoxEls[i].style.position = 'static' // 重置下position
          var height = this.imgBoxEls[i].offsetHeight
          this.colsHeightArr.push(height)
        }
      },
      initImgBoxEls () { // 初始化所有装图片的元素集合,注意高度获取需要在图片加载完成之后，所以在window.onload 事件中初始化
        this.imgBoxEls = document.getElementsByClassName('img-box')
      },
      initColumnCount () { // 列数初始化
        var winWidth = window.innerWidth
        var columnCount = parseInt(winWidth / this.colWidth)
        columnCount = columnCount === 0 ? 1 : columnCount
        this.columnCount = this.isMobile
          ? 2
          : (columnCount > this.maxCols ? this.maxCols : columnCount)
      }
    },
    mounted () {
      // ==1== 根据窗口大小初始化列数
      this.initColumnCount()
      this.beginIndex = this.columnCount // 开始排列的元素索引

      // ==2== 根据预加载完成的图片的长宽比，计算图片的高度
      this.preload()

      this.$on('preloaded', () => {
        if (this.colsHeightArr.length === 0) this.initColsHeightArr() // 第一次初始化
        this.waterfall()
      })

      window.addEventListener('resize', () => {
        // document.getElementById('#app').addEventListener('resize', () => {
        var old = this.columnCount
        this.initColumnCount()
        if (old === this.columnCount) return // 列数不变直接退出
        this.beginIndex = this.columnCount // 开始排列的元素索引

        this.initColsHeightArr()
        this.waterfall()
      })
      // console.log(this.$el.parentNode)
      // console.log(this.$el.parentNode, this.$el.parentNode.scrollTop + this.$el.parentNode.offsetHeight, this.$el.parentNode.scrollHeight)

      this.$el.parentNode.addEventListener('scroll', () => {
        if (this.isPreloading) return
        const lastImgHeight = this.imgsArr[this.imgsArr.length - 1].height
        // console.log(this.$el.parentNode, this.$el.parentNode.scrollTop + this.$el.parentNode.offsetHeight, this.$el.parentNode.scrollHeight)

        if (this.$el.parentNode.scrollTop + this.$el.parentNode.offsetHeight > this.$el.parentNode.scrollHeight - lastImgHeight) {
          this.$emit('scrollLoadImg')
        }
      })
    },
    watch: {
      imgsArr (newV, oldV) {
        if (newV.length === oldV.length) return
        this.isPreloading = true // 预加载新的图片资源
        this.preload()

        // setTimeout(()=>{ // 模拟图片预加载时间为1s

        // this.preload()
        // },1000)
      },
      isPreloading (v) {
        if (v) {
          setTimeout(() => {
            if (!this.isPreloading) return // 500毫秒内预加载完图片则不显示加载动画
            this.isPreloadingC = true
          }, this.timeOut)
        } else {
          this.isPreloadingC = false
        }
      }
    }
  }
</script>

<style lang="scss">
  .vue-waterfall-easy {
    overflow: auto;
    position: relative;
    width: 100%; // 移动端生效
    min-height: 10px;
    .img-box {
      display: inline-block;
      width: 50%; // 移动端生效
      box-sizing: border-box;
      /*float: left; // 首行排版*/
      transition: left 1s, top 1s;

      .img-inner-box {
        box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
        .img-wraper {
          width: 100%;
          background: yellow;
        }
        img {
          width: 100%;
          vertical-align: bottom;
        }
        .img-info {
          background: #fff;
          padding: .6em;
        }
      }
    }
    .loading {
      text-align: center;
      width: 100%;
      position: fixed;
      bottom: 10px;
      left: 50%;
      margin-left: -15px;
      width: 30px;
      height: 30px;
    }
    .loading.first-loading {
      //  首次预加载白屏，让加载动画处于中间
      top: 50%;
      margin-top: -15px;
    }
    .double-bounce1,
    .double-bounce2 {
      width: 100%;
      height: 100%;
      border-radius: 50%;
      background-color: #67CF22;
      opacity: 0.6;
      position: absolute;
      top: 0;
      left: 0;

      animation: bounce 2.0s infinite ease-in-out;
    }

    .double-bounce2 {
      animation-delay: -1.0s;
    }

    @keyframes bounce {
      0%,
      100% {
        transform: scale(0.0);
      }
      50% {
        transform: scale(1.0);
      }
    }
  }

  /*.clearfix:after {*/
    /*content: ".";*/
    /*display: block;*/
    /*height: 0;*/
    /*clear: both;*/
    /*visibility: hidden*/
  /*}*/
  /*.clearfix {*/
    /*zoom: 1;*/
    /*display: inline-block;*/
    /*_height: 1px*/
  /*}*/
  /*.clearfix {*/
    /*display: block;*/
  /*}*/

</style>
